package lg.minecraft.plugin.pokemonmenu.util.pokemondata.storage;

import com.cobblemon.mod.common.pokemon.Pokemon;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lg.minecraft.plugin.pokemonmenu.LGPokemonMenu;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.sql.*;
import java.util.UUID;

/**
 * @author langle__
 * @version 1.0
 */
public class MYSQLStorage extends PokemonDataStorage {

    private static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS pokemon_data (uuid VARCHAR(36) PRIMARY KEY, pokemon TEXT)";
    private static final String SELECT_POKEMON_SQL = "SELECT pokemon FROM pokemon_data WHERE uuid = ?";
    private static final String UPSERT_POKEMON_SQL = "INSERT INTO pokemon_data (uuid, pokemon) VALUES (?, ?) ON DUPLICATE KEY UPDATE pokemon = VALUES(pokemon)";
    private static final String DELETE_POKEMON_SQL = "DELETE FROM pokemon_data WHERE uuid = ?";

    private final String url;
    private final String user;
    private final String password;

    private HikariDataSource dataSource;

    public MYSQLStorage(String url, String user, String password) {
        this.url = url;
        this.user = user;
        this.password = password;
        initialize();
    }

    private void initialize() {
        try {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl(url);
            config.setUsername(user);
            config.setPassword(password);
            config.setMaximumPoolSize(10);
            config.setMinimumIdle(2);
            config.setConnectionTimeout(30000);
            config.setIdleTimeout(600000);
            config.setMaxLifetime(1800000);

            this.dataSource = new HikariDataSource(config);

            try (
                    Connection connection = dataSource.getConnection();
                    Statement statement = connection.createStatement()
            ) {
                statement.executeUpdate(CREATE_TABLE_SQL);
            }
        } catch (Exception e) {
            LGPokemonMenu.getInstance().getLogger().severe(e.toString());
        }
    }

    @Override
    public @Nullable Pokemon getPokemon(@NotNull UUID uuid) {
        try (
                Connection connection = dataSource.getConnection();
                PreparedStatement prepared = connection.prepareStatement(SELECT_POKEMON_SQL)
        ) {
            prepared.setString(1, uuid.toString());
            ResultSet result = prepared.executeQuery();
            if (result.next()) {
                String pokemonData = result.getString("pokemon");
                return fromBase64(pokemonData);
            } else {
                return null;
            }
        } catch (SQLException e) {
            LGPokemonMenu.getInstance().getLogger().severe(e.toString());
        }
        return null;
    }

    @Override
    public boolean savePokemon(@NotNull UUID uuid, @NotNull Pokemon pokemon) {
        try (
                Connection connection = dataSource.getConnection();
                PreparedStatement prepared = connection.prepareStatement(UPSERT_POKEMON_SQL)
        ) {
            prepared.setString(1, uuid.toString());
            prepared.setString(2, toBase64(pokemon));
            prepared.executeUpdate();
            return true;
        } catch (SQLException e) {
            LGPokemonMenu.getInstance().getLogger().severe(e.toString());
        }
        return false;
    }

    @Override
    public boolean removePokemon(@NotNull UUID uuid) {
        try (
                Connection connection = dataSource.getConnection();
                PreparedStatement prepared = connection.prepareStatement(DELETE_POKEMON_SQL)
        ) {
            prepared.setString(1, uuid.toString());
            prepared.executeUpdate();
            return true;
        } catch (SQLException e) {
            LGPokemonMenu.getInstance().getLogger().severe(e.toString());
        }
        return false;
    }

    public void close() {
        try {
            if (dataSource != null && !dataSource.isClosed()) {
                dataSource.close();
            }
        } catch (Exception e) {
            LGPokemonMenu.getInstance().getLogger().severe(e.toString());
        }
    }

}
